Skip to content

Feat/TO-540: Add passkey controller#8422

Open
tanguyenvn wants to merge 42 commits intomainfrom
feat/TO-540-passkey-controller
Open

Feat/TO-540: Add passkey controller#8422
tanguyenvn wants to merge 42 commits intomainfrom
feat/TO-540-passkey-controller

Conversation

@tanguyenvn
Copy link
Copy Markdown

@tanguyenvn tanguyenvn commented Apr 10, 2026

Description

Introduces @metamask/passkey-controller, a BaseController-backed package that orchestrates WebAuthn passkey enrollment and authentication for vault key protection: generating ceremony options, verifying authenticator responses, HKDF-based key derivation (PRF vs userHandle), AES-256-GCM wrapping of the vault encryption key, renewal flows for password change, and state/ceremony management (including concurrent ceremonies and lifecycle clears).

Also wires the package into the monorepo (workspace/tsconfig, CODEOWNERS, teams.json, root README, lockfile).

Notes for reviewers (SimpleWebAuthn)

Extension vs Core split

  • The browser extension UI can depend on @simplewebauthn/browser for client-side helpers that run where navigator.credentials is available.
  • The extension background / service worker cannot take a normal dependency on @simplewebauthn/server: that package targets Node-oriented verification APIs and assumptions that do not fit the constrained extension background environment the same way.

To keep verification logic aligned with SimpleWebAuthn while staying dependency-appropriate in Core, this package inlines / ports the relevant server-side verification behavior (registration and authentication response verification, signature verification, CBOR/WebAuthn parsing helpers) into packages/passkey-controller/src/webauthn/ rather than adding @simplewebauthn/server as a runtime dependency. When reviewing, please treat those modules as parity-sensitive: changes should stay consistent with upstream SimpleWebAuthn semantics where we intentionally mirror them (see recent commits around verification parity).

Changelog

  • packages/passkey-controller/CHANGELOG.md — follow monorepo changelog rules; ensure yarn validate:changelog passes before merge.

Related issues

Fixes:

Testing

  • yarn workspace @metamask/passkey-controller run test
  • (Optional) yarn workspace @metamask/passkey-controller run build

Manual testing (consumers)

N/A for Core in isolation; extension integration should exercise registration, unlock, change-password / vault key renewal, wallet reset, and edge cases called out in the extension PR.


Note

High Risk
Introduces new WebAuthn verification and cryptography (HKDF/AES-GCM) for protecting and renewing the vault encryption key, which is security-critical and easy to get subtly wrong. Also adds in-memory ceremony state handling and error-code semantics that consumers must integrate correctly.

Overview
Adds a new @metamask/passkey-controller package that orchestrates WebAuthn passkey enrollment/authentication to wrap/unwrap the vault encryption key, including PRF vs userHandle key derivation (HKDF-SHA256) and AES-256-GCM vault-key protection plus a renewal flow for password changes.

Implements and tests supporting infrastructure: challenge-keyed CeremonyManager with TTL/capacity controls, internal WebAuthn parsing/verification helpers (e.g., authenticator data parsing, RP ID hash matching, signature verification), and a structured PasskeyControllerError with stable error code/message.

Wires the new package into the monorepo via README.md package listing/dependency graph updates and CODEOWNERS entries (including release-file ownership for the new package).

Reviewed by Cursor Bugbot for commit 4c08463. Bugbot is set up for automated code reviews on this repo. Configure here.

@tanguyenvn tanguyenvn changed the title Feat/to 540 passkey controller Feat/TO-540: Add passkey controller Apr 10, 2026
@tanguyenvn tanguyenvn self-assigned this Apr 10, 2026
Comment thread packages/passkey-controller/src/encoding.ts Outdated
Comment thread packages/passkey-controller/src/crypto.ts Outdated
Comment thread packages/passkey-controller/src/PasskeyController.ts Outdated
Comment thread packages/passkey-controller/src/PasskeyController.ts Outdated
Comment thread packages/passkey-controller/src/PasskeyController.ts Outdated
@tanguyenvn tanguyenvn marked this pull request as ready for review April 16, 2026 11:25
Comment thread packages/passkey-controller/src/PasskeyController.ts
Comment thread packages/passkey-controller/src/PasskeyController.ts Outdated
Comment thread packages/passkey-controller/src/webauthn/verify-signature.ts
Comment thread packages/passkey-controller/src/key-derivation.ts
Comment thread packages/passkey-controller/src/webauthn/verify-authentication-response.ts Outdated
Comment thread packages/passkey-controller/src/key-derivation.ts Outdated
Comment thread packages/passkey-controller/src/PasskeyController.ts Outdated
Comment thread packages/passkey-controller/src/PasskeyController.ts Outdated
@tanguyenvn tanguyenvn force-pushed the feat/TO-540-passkey-controller branch from a7e07f8 to 5cc58c9 Compare April 22, 2026 14:29
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5cc58c9. Configure here.

Comment thread packages/passkey-controller/src/PasskeyController.ts
Copy link
Copy Markdown
Contributor

@mcmire mcmire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't looked over the implementation of this package (if I have time, I will do that later). I just left comments based on standards for controllers and new packages in general.

Comment thread packages/passkey-controller/src/utils/index.ts Outdated
Comment thread packages/passkey-controller/src/webauthn/index.ts Outdated
Comment thread packages/passkey-controller/src/PasskeyController.ts
Comment thread packages/passkey-controller/src/PasskeyController.ts
Comment thread packages/passkey-controller/src/PasskeyController.ts Outdated
Comment thread packages/passkey-controller/src/PasskeyController.ts Outdated
Comment thread packages/passkey-controller/jest.config.js Outdated
Comment thread packages/passkey-controller/jest.environment.js
Comment thread tsconfig.build.json
Comment thread packages/passkey-controller/CHANGELOG.md Outdated
});
if (!verified || !registrationInfo) {
this.#ceremonyManager.deleteRegistrationCeremony(challenge);
throw new Error('Passkey registration verification failed');
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make error msgs constants or use error class

Comment thread packages/passkey-controller/src/PasskeyController.ts Outdated
@tanguyenvn tanguyenvn force-pushed the feat/TO-540-passkey-controller branch from af68e38 to 8a3bf04 Compare April 23, 2026 08:03
@tanguyenvn tanguyenvn requested a review from mcmire April 23, 2026 14:01
mcmire
mcmire previously approved these changes Apr 23, 2026
Copy link
Copy Markdown
Contributor

@mcmire mcmire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious whether it makes sense to extract the WebAuthn code to a separate package in the future. It seems like it could be valuable for other use cases. Something to think about.

In the meantime this looks good to me.

@tanguyenvn
Copy link
Copy Markdown
Author

@mcmire the webauthn code is ported from @simplewebauthn/server package.
If other controllers needs webauthn/passkeys, I think they can just call the passkey-controller

@BGos87
Copy link
Copy Markdown

BGos87 commented Apr 24, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8a3bf04720

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

encKey: Uint8Array;
keyDerivation: PasskeyKeyDerivation;
} {
const credentialId = registrationResponse.id;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use attested credential ID for registration key derivation

deriveKeyFromRegistrationResponse derives the vault wrapping key from registrationResponse.id, which is not covered by the attestation signature, while the persisted credential ID later comes from parsed authenticator data. A malformed/tampered response where id/rawId differs from the attested credential ID will still pass verification and produce an encryption key tied to the wrong salt, permanently breaking subsequent unlock/decrypt for that enrollment. Derive from the verified attested credential ID (or explicitly reject mismatches) before encrypting and storing state.

Useful? React with 👍 / 👎.

residentKey: 'preferred',
},
hints: ['client-device', 'hybrid'],
attestation: 'direct',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Request an attestation mode the verifier can actually accept

Registration options always request attestation: 'direct', but verification only accepts fmt === 'none' or packed self-attestation and explicitly rejects packed x5c chains/other direct formats. A compliant authenticator can legitimately return those direct attestations, so enrollment will fail on those devices even though the browser followed requested options. Either request 'none' here or add verifier support for the direct attestation formats you are asking clients to produce.

Useful? React with 👍 / 👎.

@tanguyenvn
Copy link
Copy Markdown
Author

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@tanguyenvn
Copy link
Copy Markdown
Author

@metamaskbot publish-previews

@github-actions
Copy link
Copy Markdown
Contributor

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/account-tree-controller@7.1.0-preview-4c0846313
@metamask-previews/accounts-controller@37.2.0-preview-4c0846313
@metamask-previews/address-book-controller@7.1.1-preview-4c0846313
@metamask-previews/ai-controllers@0.6.3-preview-4c0846313
@metamask-previews/analytics-controller@1.0.1-preview-4c0846313
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-4c0846313
@metamask-previews/announcement-controller@8.1.0-preview-4c0846313
@metamask-previews/app-metadata-controller@2.0.1-preview-4c0846313
@metamask-previews/approval-controller@9.0.1-preview-4c0846313
@metamask-previews/assets-controller@6.0.0-preview-4c0846313
@metamask-previews/assets-controllers@104.2.0-preview-4c0846313
@metamask-previews/authenticated-user-storage@1.0.0-preview-4c0846313
@metamask-previews/base-controller@9.1.0-preview-4c0846313
@metamask-previews/base-data-service@0.1.1-preview-4c0846313
@metamask-previews/bridge-controller@70.1.1-preview-4c0846313
@metamask-previews/bridge-status-controller@70.0.5-preview-4c0846313
@metamask-previews/build-utils@3.0.4-preview-4c0846313
@metamask-previews/chain-agnostic-permission@1.5.0-preview-4c0846313
@metamask-previews/chomp-api-service@1.0.0-preview-4c0846313
@metamask-previews/claims-controller@0.5.0-preview-4c0846313
@metamask-previews/client-controller@1.0.1-preview-4c0846313
@metamask-previews/compliance-controller@2.0.0-preview-4c0846313
@metamask-previews/composable-controller@12.0.1-preview-4c0846313
@metamask-previews/config-registry-controller@0.2.0-preview-4c0846313
@metamask-previews/connectivity-controller@0.2.0-preview-4c0846313
@metamask-previews/controller-utils@11.20.0-preview-4c0846313
@metamask-previews/core-backend@6.2.1-preview-4c0846313
@metamask-previews/delegation-controller@3.0.0-preview-4c0846313
@metamask-previews/earn-controller@12.0.0-preview-4c0846313
@metamask-previews/eip-5792-middleware@3.0.3-preview-4c0846313
@metamask-previews/eip-7702-internal-rpc-middleware@0.1.0-preview-4c0846313
@metamask-previews/eip1193-permission-middleware@1.0.3-preview-4c0846313
@metamask-previews/ens-controller@19.1.1-preview-4c0846313
@metamask-previews/eth-block-tracker@15.0.1-preview-4c0846313
@metamask-previews/eth-json-rpc-middleware@23.1.1-preview-4c0846313
@metamask-previews/eth-json-rpc-provider@6.0.1-preview-4c0846313
@metamask-previews/foundryup@1.0.1-preview-4c0846313
@metamask-previews/gas-fee-controller@26.1.1-preview-4c0846313
@metamask-previews/gator-permissions-controller@4.0.0-preview-4c0846313
@metamask-previews/geolocation-controller@0.1.2-preview-4c0846313
@metamask-previews/json-rpc-engine@10.2.4-preview-4c0846313
@metamask-previews/json-rpc-middleware-stream@8.0.8-preview-4c0846313
@metamask-previews/keyring-controller@25.2.0-preview-4c0846313
@metamask-previews/logging-controller@8.0.1-preview-4c0846313
@metamask-previews/message-manager@14.1.1-preview-4c0846313
@metamask-previews/messenger@1.1.1-preview-4c0846313
@metamask-previews/messenger-cli@0.2.0-preview-4c0846313
@metamask-previews/money-account-balance-service@0.2.0-preview-4c0846313
@metamask-previews/money-account-controller@0.1.0-preview-4c0846313
@metamask-previews/money-account-upgrade-controller@1.0.0-preview-4c0846313
@metamask-previews/multichain-account-service@8.0.1-preview-4c0846313
@metamask-previews/multichain-api-middleware@2.0.0-preview-4c0846313
@metamask-previews/multichain-network-controller@3.0.6-preview-4c0846313
@metamask-previews/multichain-transactions-controller@7.0.4-preview-4c0846313
@metamask-previews/name-controller@9.1.1-preview-4c0846313
@metamask-previews/network-controller@30.0.1-preview-4c0846313
@metamask-previews/network-enablement-controller@5.0.2-preview-4c0846313
@metamask-previews/notification-services-controller@23.1.0-preview-4c0846313
@metamask-previews/passkey-controller@0.0.0-preview-4c0846313
@metamask-previews/permission-controller@12.3.0-preview-4c0846313
@metamask-previews/permission-log-controller@5.1.0-preview-4c0846313
@metamask-previews/perps-controller@3.2.0-preview-4c0846313
@metamask-previews/phishing-controller@17.1.1-preview-4c0846313
@metamask-previews/polling-controller@16.0.4-preview-4c0846313
@metamask-previews/preferences-controller@23.1.0-preview-4c0846313
@metamask-previews/profile-metrics-controller@3.1.3-preview-4c0846313
@metamask-previews/profile-sync-controller@28.0.2-preview-4c0846313
@metamask-previews/ramps-controller@13.2.0-preview-4c0846313
@metamask-previews/rate-limit-controller@7.0.1-preview-4c0846313
@metamask-previews/react-data-query@0.2.0-preview-4c0846313
@metamask-previews/remote-feature-flag-controller@4.2.0-preview-4c0846313
@metamask-previews/sample-controllers@4.0.4-preview-4c0846313
@metamask-previews/seedless-onboarding-controller@9.1.0-preview-4c0846313
@metamask-previews/selected-network-controller@26.1.0-preview-4c0846313
@metamask-previews/shield-controller@5.1.1-preview-4c0846313
@metamask-previews/signature-controller@39.2.0-preview-4c0846313
@metamask-previews/social-controllers@2.0.0-preview-4c0846313
@metamask-previews/storage-service@1.0.1-preview-4c0846313
@metamask-previews/subscription-controller@6.1.2-preview-4c0846313
@metamask-previews/transaction-controller@64.3.0-preview-4c0846313
@metamask-previews/transaction-pay-controller@19.2.1-preview-4c0846313
@metamask-previews/user-operation-controller@41.2.0-preview-4c0846313

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants